﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using static Devonline.Core.Helper;

namespace Devonline.Core
{
    public static class TypeExtensions
    {
        /// <summary>
        /// 是否为可空类型
        /// </summary>
        /// <param name="type">待判断的类型</param>
        /// <returns>判断结果</returns>
        public static bool IsNullable(this Type type)
        {
            ThrowWhenNull(type, nameof(type));

            return !type.IsValueType || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
        }
        /// <summary>
        /// 获取类型的核心类型, 用于判断可空类型的基本类型
        /// </summary>
        /// <param name="type">原始类型</param>
        /// <returns>核心类型</returns>
        public static Type GetCoreType(this Type type)
        {
            if (type != null && IsNullable(type))
            {
                return !type.IsValueType ? type : Nullable.GetUnderlyingType(type);
            }
            else
            {
                return type;
            }
        }
        /// <summary>
        /// 获取泛型类型的泛型参数类型
        /// </summary>
        /// <param name="type">原始类型</param>
        /// <returns>泛型参数类型</returns>
        public static Type GetGenericType(this Type type)
        {
            ThrowWhenNull(type, nameof(type));

            if (!type.IsGenericType)
            {
                Type baseType = type.BaseType;
                do
                {
                    baseType = baseType.BaseType;
                }
                while (baseType != null && baseType.BaseType != null && !baseType.IsGenericType);

                if (baseType != null && baseType.IsGenericType)
                {
                    type = baseType;
                }
            }

            if (type.IsGenericType)
            {
                return type.GenericTypeArguments.FirstOrDefault().GetCoreType();
            }

            return type.GetCoreType();
        }
        /// <summary>
        /// get the most inner base type of a type
        /// </summary>
        /// <param name="type">type instance</param>
        /// <returns>the most inner type</returns>
        public static Type GetBaseType(this Type type)
        {
            if (type?.BaseType != null)
            {
                return type.BaseType.GetBaseType();
            }

            return type;
        }
        /// <summary>
        /// to judge a type is from type or inherit from type 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="from"></param>
        /// <returns>bool</returns>
        public static bool IsFromType(this Type type, Type from)
        {
            ThrowWhenNull(type, nameof(type));
            ThrowWhenNull(from, nameof(from));

            if (type.BaseType != null)
            {
                return type.BaseType.IsFromType(from);
            }

            return false;
        }
        /// <summary>
        /// to judge a type is from type or inherit from type 
        /// </summary>
        /// <param name="type"></param>
        /// <typeparam name="TType">from type 的类型参数</typeparam>
        /// <returns>bool</returns>
        public static bool IsFromType<TType>(this Type type)
        {
            ThrowWhenNull(type, nameof(type));

            var from = typeof(TType);
            if (type == from || from.IsAssignableFrom(type) || type.IsSubclassOf(from))
            {
                return true;
            }

            if (type.BaseType != null)
            {
                return type.BaseType.IsFromType<TType>();
            }

            return false;
        }
        /// <summary>
        /// 判断一个类型是否一个集合类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsCollection(this Type type) => type.IsFromType<IEnumerable>();
        /// <summary>
        /// 判断一个类型是否泛型类型 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsGenericType(this Type type)
        {
            ThrowWhenNull(type, nameof(type));

            if (!type.IsGenericType)
            {
                do
                {
                    type = type.BaseType;
                }
                while (type.BaseType != null && !type.IsGenericType);

                return type.IsGenericType;
            }

            return true;
        }

        /// <summary>
        /// 获取对象上的显示名称(Display 或 DisplayName 或 Description 特性的值)
        /// </summary>
        /// <param name="member">待获取对象</param>
        /// <returns>显示名称</returns>
        public static string GetDisplayName([NotNull] this MemberInfo member) => member?.GetCustomAttribute<DisplayAttribute>()?.Name ?? member?.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? member?.GetCustomAttribute<DescriptionAttribute>()?.Description ?? member.Name;
        /// <summary>
        /// 通过当前类型的公开实例成员获取成员标注的 Display/DisplayName 特性的值
        /// </summary>
        /// <param name="type"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static string GetDisplayName(this Type type, string memberName) => type.GetMember(memberName, BindingFlags.Public | BindingFlags.Instance)?.FirstOrDefault()?.GetDisplayName();
        /// <summary>
        /// 获取对象上的隐射到数据库的字段名字(Column 特性的 Name 值)
        /// </summary>
        /// <param name="member">待获取对象</param>
        /// <returns>字段名称</returns>
        public static string GetColumnName(this MemberInfo member) => member?.GetCustomAttribute<ColumnAttribute>()?.Name ?? member.Name;
        /// <summary>
        /// 获取对象上的隐射到数据库的字段名字(Table 特性的 Name 值)
        /// </summary>
        /// <param name="member">待获取对象</param>
        /// <returns>字段名称</returns>
        public static string GetTableName(this MemberInfo member) => member?.GetCustomAttribute<TableAttribute>()?.Name ?? member.Name;
        /// <summary>
        /// 获取类型字段设置的 json 序列化字段名
        /// </summary>
        /// <param name="type"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static string GetJsonPropertyName(this Type type, string memberName)
        {
            ThrowWhenNull(type, nameof(type));

            var member = type.GetMember(memberName)?.FirstOrDefault();
            return member?.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? member?.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name ?? member.Name;
        }

        /// <summary>
        /// 返回成员的真实类型
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Type GetMemberType(this MemberInfo member) => member switch
        {
            PropertyInfo propertyInfo => propertyInfo.PropertyType,
            FieldInfo fieldInfo => fieldInfo.FieldType,
            Type type => type,
            _ => null
        };

        /// <summary>
        /// 获取类型的实例
        /// </summary>
        /// <param name="type"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static object CreateInstance(this Type type, params object[] parameters) => Activator.CreateInstance(type, parameters);
        /// <summary>
        /// 反射调用类型的静态泛型方法
        /// </summary>
        /// <param name="type">调用类型</param>
        /// <param name="methodName">方法名</param>
        /// <param name="parameters">参数列表</param>
        /// <param name="returnValue">返回值</param>
        /// <param name="genericTypes">泛型类型</param>
        public static void InvokeGenericMethod(this Type type, string methodName, object[] parameters, out object returnValue, params Type[] genericTypes)
        {
            returnValue = null;
            var methodInfo = type?.GetMethods().FirstOrDefault(x => x.IsGenericMethod && x.Name == methodName && x.GetParameters().Length == (parameters?.Length ?? 0));
            if (methodInfo != null)
            {
                returnValue = methodInfo.MakeGenericMethod(genericTypes).Invoke(null, parameters);
            }
        }
        /// <summary>
        /// 反射调用类型的静态泛型方法
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="type">调用类型</param>
        /// <param name="methodName">方法名</param>
        /// <param name="parameters">参数列表</param>
        /// <param name="genericTypes">泛型类型</param>
        /// <returns>返回调用结果</returns>
        public static T InvokeGenericMethod<T>(this Type type, string methodName, object[] parameters, params Type[] genericTypes) where T : class
        {
            var methodInfo = type?.GetMethods().FirstOrDefault(x => x.IsGenericMethod && x.Name == methodName && x.GetParameters().Length == (parameters?.Length ?? 0));
            if (methodInfo != null)
            {
                return (T)methodInfo.MakeGenericMethod(genericTypes).Invoke(null, parameters);
            }

            return default;
        }
        /// <summary>
        /// 反射调用类型的泛型扩展方法
        /// 预处理调用者参数, 因此不需要传递调用者参数
        /// </summary>
        /// <param name="type">调用类型</param>
        /// <param name="methodName">方法名</param>
        /// <param name="caller">调用对象</param>
        /// <param name="parameters">参数列表</param>
        /// <param name="returnValue">返回值</param>
        /// <param name="genericTypes">泛型类型</param>
        public static void InvokeGenericMethod(this Type type, string methodName, object caller, object[] parameters, out object returnValue, params Type[] genericTypes)
        {
            returnValue = null;
            var methodInfo = type?.GetTypeInfo().GetDeclaredMethods(methodName).FirstOrDefault(x => x.IsGenericMethod && x.GetParameters().Length == (parameters?.Length ?? 0) + 1);
            if (methodInfo != null)
            {
                var args = new List<object>
                {
                    caller
                };

                if (parameters.IsNotNullOrEmpty())
                {
                    args.AddRange(parameters);
                }

                returnValue = methodInfo.MakeGenericMethod(genericTypes).Invoke(caller, args.ToArray());
            }
        }
        /// <summary>
        /// 反射调用类型的泛型扩展方法
        /// 预处理调用者参数, 因此不需要传递调用者参数
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="type">调用类型</param>
        /// <param name="methodName">方法名</param>
        /// <param name="caller">调用对象</param>
        /// <param name="parameters">参数列表</param>
        /// <param name="genericTypes">泛型类型</param>
        /// <returns>返回调用结果</returns>
        public static T InvokeGenericMethod<T>(this Type type, string methodName, object caller, object[] parameters, params Type[] genericTypes) where T : class
        {
            var methodInfo = type?.GetTypeInfo().GetDeclaredMethods(methodName).FirstOrDefault(x => x.IsGenericMethod && x.GetParameters().Length == (parameters?.Length ?? 0) + 1);
            if (methodInfo != null)
            {
                var args = new List<object>
                {
                    caller
                };

                if (parameters.IsNotNullOrEmpty())
                {
                    args.AddRange(parameters);
                }

                return (T)methodInfo.MakeGenericMethod(genericTypes).Invoke(caller, args.ToArray());
            }

            return default;
        }

        /// <summary>
        /// 判断成员中是否包含特性
        /// </summary>
        /// <typeparam name="T">类型参数, Attribute 类型</typeparam>
        /// <param name="member">带判断成员</param>
        /// <param name="inherit">是否包含继承的特性</param>
        /// <returns>判断结果</returns>
        public static bool HasAttribute<T>(this MemberInfo member, bool inherit = true) where T : Attribute => member.IsDefined(typeof(T), inherit);
        /// <summary>
        /// 获取成员特性的指定属性值
        /// </summary>
        /// <param name="member">待获取对象</param>
        /// <param name="propertyName">用于获取值的特性上的属性名称</param>
        /// <returns>显示名称</returns>
        public static TValue GetAttributeValue<TAttribute, TValue>(this MemberInfo member, string propertyName) where TAttribute : Attribute => (TValue)member?.GetCustomAttribute<TAttribute>()?.GetPropertyValue(propertyName);

        /// <summary>
        /// 获取属性上设置的 excel 特性内容
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static ExcelAttribute GetExcelAttribute(this MemberInfo memberInfo)
        {
            if (memberInfo == null || !(memberInfo.MemberType == MemberTypes.Property || memberInfo.MemberType == MemberTypes.Field))
            {
                throw new ArgumentException("成员类型只能是属性或者字段, 且不可为空!", nameof(memberInfo));
            }

            var attribute = memberInfo.GetCustomAttribute<ExcelAttribute>(true);
            if (attribute != null)
            {
                attribute.Field ??= memberInfo;
                var name = memberInfo.GetAttributeValue<DisplayAttribute, string>(nameof(DisplayAttribute.Name))
                    ?? memberInfo.GetAttributeValue<DisplayNameAttribute, string>(nameof(DisplayNameAttribute.DisplayName))
                    ?? memberInfo.GetAttributeValue<DescriptionAttribute, string>(nameof(DescriptionAttribute.Description));
                attribute.Name ??= name ?? attribute.Field.Name;
                attribute.FieldType = memberInfo.MemberType == MemberTypes.Property ? (memberInfo as PropertyInfo).PropertyType : (memberInfo as FieldInfo).FieldType;
                attribute.Required = !attribute.FieldType.IsNullable() || memberInfo.HasAttribute<RequiredAttribute>();
                attribute.FieldType = attribute.FieldType.GetCoreType();

                switch (Type.GetTypeCode(attribute.FieldType))
                {
                    case TypeCode.DateTime:
                        attribute.Format ??= AppSettings.DEFAULT_DATETIME_FORMAT;
                        attribute.Size = attribute.Size == 0 ? AppSettings.DEFAULT_EXCEL_DATETIME_SIZE : attribute.Size;
                        break;
                    case TypeCode.Byte:
                    case TypeCode.SByte:
                    case TypeCode.UInt16:
                    case TypeCode.Int16:
                    case TypeCode.UInt32:
                    case TypeCode.Int32:
                    case TypeCode.UInt64:
                    case TypeCode.Int64:
                        attribute.Format ??= AppSettings.DEFAULT_EXCEL_NUMBER_FORMAT;
                        attribute.Size = attribute.Size == 0 ? AppSettings.DEFAULT_EXCEL_NUMBER_SIZE : attribute.Size;
                        break;
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        attribute.Format ??= AppSettings.DEFAULT_EXCEL_DOUBLE_FORMAT;
                        attribute.Size = attribute.Size == 0 ? AppSettings.DEFAULT_EXCEL_DOUBLE_SIZE : attribute.Size;
                        break;
                    case TypeCode.Empty:
                    case TypeCode.Boolean:
                    case TypeCode.Char:
                        attribute.Format ??= AppSettings.DEFAULT_EXCEL_STRING_FORMAT;
                        attribute.Size = attribute.Size == 0 ? AppSettings.DEFAULT_EXCEL_NUMBER_SIZE : attribute.Size;
                        break;
                    case TypeCode.DBNull:
                    case TypeCode.Object:
                    case TypeCode.String:
                    default:
                        attribute.Format ??= AppSettings.DEFAULT_EXCEL_STRING_FORMAT;
                        attribute.Size = attribute.Size == 0 ? AppSettings.DEFAULT_EXCEL_STRING_SIZE : attribute.Size;
                        attribute.MaxLength = memberInfo.GetCustomAttribute<MaxLengthAttribute>()?.Length ?? 0;
                        break;
                }
            }

            return attribute;
        }

        /// <summary>
        /// 获取属性或字段的 field 描述
        /// </summary>
        /// <param name="memberInfo"></param>
        /// <returns></returns>
        public static FieldAttribute GetFieldAttribute(this MemberInfo memberInfo)
        {
            ThrowWhenNull(memberInfo, nameof(memberInfo));

            var attribute = memberInfo.GetCustomAttribute<FieldAttribute>(true);
            if (attribute != null)
            {
                attribute.Name ??= memberInfo.Name;
                attribute.Doc ??= memberInfo.GetDisplayName();
                if (attribute.Type == TypeCode.Empty)
                {
                    attribute.Type = Type.GetTypeCode(memberInfo.DeclaringType);
                }

                attribute.Size = attribute.Type switch
                {
                    TypeCode.Boolean or TypeCode.Byte or TypeCode.SByte => 1,
                    TypeCode.Char or TypeCode.Int16 or TypeCode.UInt16 => 2,
                    TypeCode.Int32 or TypeCode.UInt32 or TypeCode.Single => 4,
                    TypeCode.Int64 or TypeCode.UInt64 or TypeCode.Double or TypeCode.Decimal => 8,
                    _ => attribute.Size
                };
            }

            return attribute;
        }
        /// <summary>
        /// 获取类型的所有字段 field 特性值
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static ICollection<FieldAttribute> GetFieldAttributes(this Type type) => type?.GetMembers().Where(x => x.HasAttribute<FieldAttribute>()).Select(x => x.GetFieldAttribute()).ToList();
    }
}
